package edu.ruc.cluster.util;

import java.io.File;
import java.io.FileInputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.tools.tar.TarEntry;
import org.apache.tools.tar.TarInputStream;
import org.apache.tools.tar.TarOutputStream;

/**
 * 2012-12-12
 * Used:
 * 		compress certain file(folder or File) as .tar.gz
 * Call Example:
 * 		TarGzGenerator targz = new TarGzGenerator();
 * 		targz.getTarGz("/some/source/path", "/dest/tar/gz/file");
 * @author deke
 *
 */
public class TarGzGenerator {

	private static DefaultLogHandler logHandler = null;
	public TarGzGenerator(){
		logHandler = DefaultLogHandler.getInstance();
	}
	
	private void tar(File file, TarOutputStream out, String dir, boolean boo){
		
		try{
			if(file.isDirectory()){
				File[] listfile = file.listFiles();
				if(listfile.length == 0 && boo){
					out.putNextEntry(new TarEntry(dir + file.getName() + "/"));
//					System.out.println("tar " + dir + file.getName() + "/");
					return;
				}else{
					for(File cfile : listfile){
						tar(cfile, out, dir + file.getName() + "/", boo);
					}
				}
			}else if(file.isFile()){
//				System.out.println("tar " + dir + file.getName() );
				byte[] bt = new byte[2048 * 2];
				TarEntry ze = new TarEntry(dir + file.getName());
				ze.setSize(file.length());
				// If do not set this, length(ze) > 100 byte would cause problems!
				out.setLongFileMode(TarOutputStream.LONGFILE_GNU);
				out.putNextEntry(ze);
				FileInputStream fis = null;
				try{
					fis = new FileInputStream(file);
					int i = 0;
					while((i = fis.read(bt)) != -1){
						out.write(bt, 0, i);
					}
				}catch(IOException e){
					logHandler.error(e.getMessage(), e);
				}finally{
					try{
						if(fis != null)
							fis.close();
						out.closeEntry();
					}catch(IOException e){
						logHandler.error(e.getMessage(), e);
					}
				}
			}
		}catch(IOException e){
			logHandler.error(e.getMessage(), e);
		}
	}

	private File testTar(String src, String dst){
		
		File srcFile = new File(src);		// like "c:/upload"	
		File targetTarFile = new File(dst); // like "c:/upload.tar"		
		TarOutputStream out = null;		
		boolean boo = false;
		
		try{
			if(targetTarFile.exists())
				targetTarFile.delete();
			out = new TarOutputStream(new BufferedOutputStream(new FileOutputStream(targetTarFile)));			
			tar(srcFile, out, "", true);			
			boo = true;			
			return targetTarFile;	
			
		}catch(IOException ex){
			throw new RuntimeException(ex);
		}finally{			
			try{
				if(out!=null)
					out.close();
			}catch(IOException ex){
				throw new RuntimeException("Exception in close TarOutputStream",ex);
			}finally{
				//close
				if(!boo && targetTarFile.exists())
					targetTarFile.delete();				
			}			
		}		
	}	
	
	public void getTarGz(String src, String dst) throws RuntimeException{
		
		File tarFile = testTar(src, dst.substring(0, dst.length() - 3));		
		File gzFile = new File(tarFile + ".gz");		
		GZIPOutputStream out = null;		
		InputStream in = null;		
		boolean boo = false;
		
		try{			
			in = new FileInputStream(tarFile);			
			out = new GZIPOutputStream(new FileOutputStream(gzFile),1024 * 2);			
			byte b[] = new byte[1024 * 2];			
			int length = 0;			
			while( (length = in.read(b)) != -1 ){				
				out.write(b,0,length);
			}			
			boo = true;
			
		}catch(Exception ex){			
			throw new RuntimeException("GZIP Exception",ex);
		}finally{			
			try{
				if(out!=null)
					out.close();
				if(in!=null)
					in.close();
				if(tarFile.exists())
					tarFile.delete();
			}catch(IOException ex){
				throw new RuntimeException("Close Exception",ex);
			}finally{				
				if(!boo){					
					tarFile.delete();					
					if(gzFile.exists())
						gzFile.delete();					
				}				
			}	
			
		}		
	}
	
	public void getUnTarGz(String src, String dst) throws RuntimeException{
		File srcTarGzFile = new File(src);
		boolean boo = false;
		try{
			logHandler.info("going to run UnTar:");
			unTarGz(srcTarGzFile, dst);
			boo = true;
		}catch(IOException e){
			throw new RuntimeException(e);
		}finally{
			if(!boo)
				deleteDirectory(new File(dst));
		}
	}
	
	private void unTarGz(File file, String outputDir) throws IOException{
		TarInputStream tarIn = null;
		try{
			tarIn = new TarInputStream(new GZIPInputStream(
					new BufferedInputStream(new FileInputStream(file))), 1024*2);
//			logHandler.info("create directory");
			createDirectory(outputDir, null);
			TarEntry entry = null;
			while((entry = tarIn.getNextEntry()) != null){
				if(entry.isDirectory()){
					createDirectory(outputDir, entry.getName());
				}else{
					File tmpFile = new File(outputDir + "/" + entry.getName());
					createDirectory(tmpFile.getParent() + "/", null);
					OutputStream out = null;
					try{
						out = new FileOutputStream(tmpFile);
						int length = 0;
						byte[] b = new byte[2048];
						while((length = tarIn.read(b)) != -1){
							out.write(b, 0, length);
						}
					}catch(IOException ex){
						throw ex;
					}finally{
						if(out != null)
							out.close();
					}
				}
			}
		}catch(IOException e){
			throw new IOException("tar exception!", e);
		}finally{
			try{
				if(tarIn != null)
					tarIn.close();
			}catch(IOException e){
				throw new IOException("close tarFile Exception", e);
			}
		}
	}
	
	private void createDirectory(String outputDir, String subDir){
		File file = new File(outputDir);
		if(!(subDir == null || subDir.trim().equals(""))){
			file = new File(outputDir + "/" + subDir);
			logHandler.info("create directory " + outputDir + "/" + subDir);
		}
		if(!file.exists())
			file.mkdirs();
	}
	
	private void deleteDirectory(File file){
		if(file.isFile())
			file.delete();
		else{
			File list[] = file.listFiles();
			if(list != null){
				for(File f : list)
					deleteDirectory(f);
				file.delete();
			}
		}
	}
	/*
	public static void main(String[] args){
		TarGzGenerator gz = new TarGzGenerator();
		String filename = 
				"/home/deke/yt/software/testtar";
//				"/home/deke/yt/software/testtar/pssh-2.1.1.tar.gz";
		File file = new File(filename);
		System.out.println(file.getName());
//		gz.getUnTarGz(filename, "hadoop");
	}
	*/
}
